V8 采用的垃圾回收算法 —— 分代回收(Generation GC)

# 一. 什么是垃圾

确定不会再被使用的数据,即为垃圾数据。

栈(stack)会自动分配内存空间,会自动释放空间。无需垃圾回收。 堆(heap)动态分配的内存,大小不定也不会自动释放。需要垃圾回收机制来释放内存空间。 :闭包中的变量并不保存在栈内存中,而是保存在堆内存中。这也是闭包能引用到函数内变量的原因。

# 二、算法分类

常见的 JS 垃圾回收基础算法有两类:标记清除引用计数

由于基础算法是对全部对象进行遍历,而JS引擎是单线程的,执行垃圾回收时页面会处在卡顿状态,执行时间越长卡顿现象越久,因此,实际各家浏览器的垃圾回收策略都是在基础算法的基础上做了优化:

V8 的垃圾回收机制:分代回收

分代回收机制:

  1. 将内存分为“新生代区”和“老生代区”,分别存储“新生代对象”和“老生代对象”;
  2. 老生代区占用的内存较大,最初存储 JS 原始对象;
  3. 新生代区占用的内存较小,且又被分为“活跃区”和“非活跃区”,最初活跃区存放所有新创建的对象,非活跃区为空;
  4. 当新生代的活跃区被占满后,将触发垃圾回收,过程如下:
  5. 交换新生代的“活跃区”和“非活跃区”
  6. 对“非活跃区”的对象进行“标记清除”,过程如下:
  * 从根节点开始遍历对象
  * 未被标记过的活跃对象放回“活跃区”
  * 已被多次标记的活跃对象放进“老生代区”
  * 标记出不再活跃的对象,然后统一清除,此时新生代的活跃区只剩下活跃的对象
  1. 当老生代被占满后,触发垃圾回收,“标记清楚”过程如下:
  2. 从根节点开始遍历对象
  3. 标记出不再活跃的非原始对象(不能清除原始对象),然后统一清除,此时老生代区只剩下原始对象和活跃的对象
  4. 垃圾回收的最后,要进行内存整理,使不连续的内存空间变为连续空间,便于再次使用

# 三、 标记清除如何回收垃圾

# 1. 标记

V8 采用 可达性 算法来判断堆中对象应不应该被回收,判断步骤如下:

  • 从根节点出发,遍历所有的对象
  • 可以遍历到的对象,标识为可达(reachable)
  • 无法遍历到的对象,标识为不可达(unreachable)

浏览器环境下,根节点包括:

  • 全局对象 window,位于每个 iframe 中
  • 文档 DOM 对象
  • 存放在栈中的变量

根节点不是垃圾,不能被回收。

# 2. 清理

标记完成后,统一清理被标识为不可达的对象。

# 3. 内存整理

清理操作后,堆内存中存在大量不连续的空间,称为 内存碎片

https://juejin.im/post/6844903695721709581#heading-15

https://jinlong.github.io/2016/05/01/4-Types-of-Memory-Leaks-in-JavaScript-and-How-to-Get-Rid-Of-Them/

https://zhuanlan.zhihu.com/p/33816534

最后更新时间: 3/28/2021, 9:04:52 PM